﻿using Autofac;
using Hprose.RPC;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace HproseRpcTester.Utils
{
    /// <summary>
    /// rpc服务（基于Hprose.RPC的封装），注意：本服务基于Autofac，将Autofac里带有RPCService特性的类中的带有RPC特性的方法映射出rpc服务
    /// </summary>
    public interface IRpcService
    {
        Hprose.RPC.Service Service { get; }
        ServiceContext Context { get; }

        #region AddMethod
        RpcService AddMethod(Action action, string fullname = null);
        RpcService AddMethod(Method method);
        RpcService AddMethod<T>(Action<T> action, string fullname = null);
        RpcService AddMethod<T1, T2, T3, T4, T5, TResult>(Func<T1, T2, T3, T4, T5, TResult> func, string fullname = null);
        RpcService AddMethod<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action, string fullname = null);
        RpcService AddMethod<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> func, string fullname = null);
        RpcService AddMethod<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action, string fullname = null);
        RpcService AddMethod<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> func, string fullname = null);
        RpcService AddMethod<T1, T2, T3>(Action<T1, T2, T3> action, string fullname = null);
        RpcService AddMethod<T1, T2, TResult>(Func<T1, T2, TResult> func, string fullname = null);
        RpcService AddMethod<T1, T2>(Action<T1, T2> action, string fullname = null);
        RpcService AddMethod<T1, TResult>(Func<T1, TResult> func, string fullname = null);
        RpcService AddMethod<TResult>(Func<TResult> func, string fullname = null);
        #endregion
        /// <summary>
        /// 配置服务
        /// </summary>
        /// <param name="url">注意：该http地址必须以/结尾</param>
        bool Configure(string url);
        /// <summary>
        /// 运行服务
        /// </summary>
        /// <returns></returns>
        bool Run();
        /// <summary>
        /// 使用InvokeHandler
        /// </summary>
        /// <param name="handlers"></param>
        /// <returns></returns>
        RpcService Use(params InvokeHandler[] handlers);
        /// <summary>
        /// 使用IOHandler
        /// </summary>
        /// <param name="handlers"></param>
        /// <returns></returns>
        RpcService Use(params IOHandler[] handlers);
    }

    /// <summary>
    /// rpc服务（基于Hprose.RPC的封装），注意：本服务基于Autofac，将Autofac里带有RPCService特性的类中的带有RPC特性的方法映射出rpc服务
    /// </summary>
    public class RpcService : IDisposable, IRpcService
    {
        private HttpListener _HttpListener = null;
        //private Logger Log = LogManager.GetCurrentClassLogger();
        public Hprose.RPC.Service Service { get; private set; } = null;
        private ServiceContext context = null;
        public ServiceContext Context => context ?? (context = new ServiceContext(Service));

        /// <summary>
        /// 初始化
        /// </summary>
        public RpcService()
        {
            _HttpListener = new HttpListener();
            Service = new Hprose.RPC.Service();
        }

        /// <summary>
        /// 配置服务
        /// </summary>
        /// <param name="url">注意：该http地址必须以/结尾</param>
        /// <returns></returns>
        public bool Configure(string url)
        {
            try
            {
                _HttpListener.Prefixes.Add(url); // 必须以'/'结尾
                return true;
            }
            catch (Exception ex)
            {
                //Log.Error(ex);
                return false;
            }
        }

        /// <summary>
        /// 运行服务
        /// </summary>
        /// <returns></returns>
        public bool Run()
        {
            try
            {
                _HttpListener.Start();
                Service.Bind(_HttpListener);
                return true;
            }
            catch (Exception ex)
            {
                //Log.Error(ex);
                return false;
            }
        }

        /// <summary>
        /// 使用InvokeHandler
        /// </summary>
        /// <param name="handlers"></param>
        /// <returns></returns>
        public RpcService Use(params InvokeHandler[] handlers)
        {
            Service.Use(handlers);
            return this;
        }

        /// <summary>
        /// 使用IOHandler
        /// </summary>
        /// <param name="handlers"></param>
        /// <returns></returns>
        public RpcService Use(params IOHandler[] handlers)
        {
            Service.Use(handlers);
            return this;
        }

        #region AddMethod
        /// <summary>
        /// 添加调用
        /// </summary>
        /// <param name="method"></param>
        /// <returns></returns>
        public RpcService AddMethod(Method method)
        {
            Service.Add(method).Bind(_HttpListener);
            return this;
        }

        /// <summary>
        /// 添加调用
        /// </summary>
        /// <param name="action"></param>
        /// <param name="fullname"></param>
        /// <returns></returns>
        public RpcService AddMethod(Action action, string fullname = null)
        {
            Service.Add(action, fullname);
            return this;
        }

        /// <summary>
        /// 添加调用
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="action"></param>
        /// <param name="fullname"></param>
        /// <returns></returns>
        public RpcService AddMethod<T>(Action<T> action, string fullname = null)
        {
            Service.Add<T>(action, fullname);
            return this;
        }

        /// <summary>
        /// 添加调用
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <param name="action"></param>
        /// <param name="fullname"></param>
        /// <returns></returns>
        public RpcService AddMethod<T1, T2>(Action<T1, T2> action, string fullname = null)
        {
            Service.Add<T1, T2>(action, fullname);
            return this;
        }

        /// <summary>
        /// 添加调用
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <typeparam name="T3"></typeparam>
        /// <param name="action"></param>
        /// <param name="fullname"></param>
        /// <returns></returns>
        public RpcService AddMethod<T1, T2, T3>(Action<T1, T2, T3> action, string fullname = null)
        {
            Service.Add<T1, T2, T3>(action, fullname);
            return this;
        }

        /// <summary>
        /// 添加调用
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <typeparam name="T3"></typeparam>
        /// <typeparam name="T4"></typeparam>
        /// <param name="action"></param>
        /// <param name="fullname"></param>
        /// <returns></returns>
        public RpcService AddMethod<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action, string fullname = null)
        {
            Service.Add<T1, T2, T3, T4>(action, fullname);
            return this;
        }

        /// <summary>
        /// 添加调用
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <typeparam name="T3"></typeparam>
        /// <typeparam name="T4"></typeparam>
        /// <typeparam name="T5"></typeparam>
        /// <param name="action"></param>
        /// <param name="fullname"></param>
        /// <returns></returns>
        public RpcService AddMethod<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action, string fullname = null)
        {
            Service.Add<T1, T2, T3, T4, T5>(action, fullname);
            return this;
        }

        /// <summary>
        /// 添加调用
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="func"></param>
        /// <param name="fullname"></param>
        /// <returns></returns>
        public RpcService AddMethod<TResult>(Func<TResult> func, string fullname = null)
        {
            Service.Add<TResult>(func, fullname);
            return this;
        }

        /// <summary>
        /// 添加调用
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="func"></param>
        /// <param name="fullname"></param>
        /// <returns></returns>
        public RpcService AddMethod<T1, TResult>(Func<T1, TResult> func, string fullname = null)
        {
            Service.Add<T1, TResult>(func, fullname);
            return this;
        }

        /// <summary>
        /// 添加调用
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="func"></param>
        /// <param name="fullname"></param>
        /// <returns></returns>
        public RpcService AddMethod<T1, T2, TResult>(Func<T1, T2, TResult> func, string fullname = null)
        {
            Service.Add<T1, T2, TResult>(func, fullname);
            return this;
        }

        /// <summary>
        /// 添加调用
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <typeparam name="T3"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="func"></param>
        /// <param name="fullname"></param>
        /// <returns></returns>
        public RpcService AddMethod<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> func, string fullname = null)
        {
            Service.Add<T1, T2, T3, TResult>(func, fullname);
            return this;
        }

        /// <summary>
        /// 添加调用
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <typeparam name="T3"></typeparam>
        /// <typeparam name="T4"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="func"></param>
        /// <param name="fullname"></param>
        /// <returns></returns>
        public RpcService AddMethod<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> func, string fullname = null)
        {
            Service.Add<T1, T2, T3, T4, TResult>(func, fullname);
            return this;
        }

        /// <summary>
        /// 添加调用
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <typeparam name="T3"></typeparam>
        /// <typeparam name="T4"></typeparam>
        /// <typeparam name="T5"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="func"></param>
        /// <param name="fullname"></param>
        /// <returns></returns>
        public RpcService AddMethod<T1, T2, T3, T4, T5, TResult>(Func<T1, T2, T3, T4, T5, TResult> func, string fullname = null)
        {
            Service.Add<T1, T2, T3, T4, T5, TResult>(func, fullname);
            return this;
        }
        #endregion

        /// <summary>
        /// 释放
        /// </summary>
        public void Dispose()
        {
            _HttpListener?.Stop();
        }
    }

    /// <summary>
    /// rpc扩展
    /// </summary>
    public static class RpcServiceExtension
    {
        private static ILog log = new ConsoleLogger();
        /// <summary>
        /// 添加rpc服务，请在需要开启rpc的方法前加上RPC特性
        /// </summary>
        /// <param name="services">服务器</param>
        public static ContainerBuilder AddRPCService(this ContainerBuilder bd)
        {
            IRpcService rpc = null;
            bd.RegisterInstance(rpc = new RpcService()).AsImplementedInterfaces();
            AutofacHelper.AddRPCInstance(typeof(IRpcService), rpc);
            return bd;
        }

        /// <summary>
        /// 启动rpc服务，请在需要开启rpc的方法前加上RPC特性，在需要开启的类上标记RPCService特性，注意：RPC服务必然是单例
        /// </summary>
        /// <param name="app"></param>
        /// <param name="port">端口号</param>
        /// <param name="ipWhite">ip白名单</param>
        public static IContainer UseRPC(this IContainer app, int port, params string[] ipWhite)
        {
            log.Debug($"开始rpc服务注册...");
            try
            {
                var rpc = app.Resolve<IRpcService>();
                string url = $"http://127.0.0.1:{port}/";
                rpc.Configure(url);
                log.Debug($"配置的服务地址：{url}");
                rpc.Run();

                if (ipWhite.Length > 0)
                    rpc.Use(new Writelist(ipWhite).Handler);
                var rt = typeof(RPCAttribute);
                var rst = typeof(RPCServiceAttribute);
                bool hasService = false;
                foreach (var item in app.ComponentRegistry.Registrations)
                {
                    try
                    {
                        if (item.Activator.LimitType.CustomAttributes.Any(x => x.AttributeType == rst))
                        {
                            log.Debug($"注册[{item.Activator.LimitType.Name}]...");
                            foreach (var s in item.Services)
                            {
                                var t = s.GetType();
                                if (t == typeof(Autofac.Core.KeyedService))
                                {
                                    var cs = (Autofac.Core.KeyedService) s;
                                    var tp = AutofacHelper.Container.Resolve(cs.ServiceType);
                                    foreach (var m in tp.GetType().GetMethods())
                                    {
                                        if (m.CustomAttributes.Any(x => x.AttributeType == rt))
                                        {
                                            hasService = true;
                                            rpc.AddMethod(new Method(m, tp));
                                        }
                                    }
                                }
                                else if (t == typeof(Autofac.Core.TypedService))
                                {
                                    var cs = (Autofac.Core.TypedService) s;
                                    var tp = AutofacHelper.Container.Resolve(cs.ServiceType);
                                    foreach (var m in tp.GetType().GetMethods())
                                    {
                                        if (m.CustomAttributes.Any(x => x.AttributeType == rt))
                                        {
                                            hasService = true;
                                            rpc.AddMethod(new Method(m, tp));
                                        }
                                    }
                                }
                                else continue;
                            }

                            log.Debug($"注册[{item.Activator.LimitType.Name}]完成");
                        }
                    }
                    catch (Exception e)
                    {
                        log.Error($"注册{item.Activator.LimitType.Name}失败: {e.Message}");
                    }
                }
                if (!hasService)
                {
                    log.Debug($"未发现rpc服务，请确保注册rpc的服务初始化正常");
                }
            }
            catch (Exception exp)
            {
                log.Debug($"注册rpc服务异常：{exp.Message}");
            }
            return app;
        }
    }

    /// <summary>
    /// 白名单
    /// </summary>
    internal class Writelist
    {
        private List<string> _Whitelist = new List<string>();

        /// <summary>
        /// 初始化白名单
        /// </summary>
        /// <param name="ip"></param>
        public Writelist(params string[] ip)
        {
            if (ip.Length <= 0)
                return;
            _Whitelist = new List<string>();
            string pattern = @"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$";
            foreach (string item in ip)
            {
                Match result = Regex.Match(item.Trim(), pattern);
                if (string.IsNullOrEmpty(result.Value))
                    continue;
                _Whitelist.Add(result.Value);
            }
        }
        /// <summary>
        /// 黑名单
        /// </summary>
        /// <returns></returns>
        public Task<object> BlackHole()
        {
            return Task.Run(() => { return new object(); });
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="args"></param>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public Task<object> Handler(string name, object[] args, Context context, NextInvokeHandler next)
        {
            if (_Whitelist.Count <= 0)
                return next(name, args, context);
            string currentIP = ((ServiceContext)context).RemoteEndPoint.ToString().Split(':')[0];
            if (!_Whitelist.Contains(currentIP))
                return BlackHole();
            return next(name, args, context);
        }
    }

    /// <summary>
    /// RPC特性
    /// </summary>
    public class RPCAttribute : Attribute { }

    /// <summary>
    /// RPC服务特性
    /// </summary>
    public class RPCServiceAttribute : Attribute { }
}
